package com.nlscan.qdkit.ui.publico.util;


import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Build;
import android.os.Handler;
import android.os.Message;

import androidx.collection.LruCache;

import com.nlscan.qdkit.ui.publico.R;
import com.nlscan.qdkit.ui.publico.task.ThreadPool;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 本地图片加载器,采用的是异步解析本地图片，单例模式利用getInstance()获取NativeImageLoader实例
 * 调用loadNativeImage()方法加载本地图片，此类可作为一个加载本地图片的工具类
 *
 * @author xiaanming
 * @blog http://blog.csdn.net/xiaanming
 */
public class NativeImageLoader {
    private Context context;
    private static NativeImageLoader mInstance;
    private static LruCache<String, Bitmap> mMemoryCache;
    private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);

    /**
     * Default disk cache size 10MB
     */
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10;

    /**
     * Compression settings when writing images to disk cache
     */
    private static final Bitmap.CompressFormat COMPRESS_FORMAT = Bitmap.CompressFormat.JPEG;

    /**
     * Disk cache index to read from
     */
    private static final int DISK_CACHE_INDEX = 0;

    /**
     * Image compression quality
     */
    private static final int COMPRESS_QUALITY = 98;
    /**
     * 图片硬盘缓存核心类。
     */
    private DiskLruCache mDiskCache;
    /**
     * 图片加载队列，后进先出
     */
    private Stack<LoadImageTask> mImageQueue = new Stack<LoadImageTask>();

    /**
     * 图片请求队列，先进先出，用于存放已发送的请求。
     */
    private Queue<LoadImageTask> mRequestQueue = new LinkedList<LoadImageTask>();
    /**
     * 请求图片
     */
    private static final int MSG_REQUEST = 1;
    /**
     * 图片加载完成
     */
    private static final int MSG_lOAD_COMPLETED = 2;
    Handler mHander;

    @SuppressLint("NewApi")
    private NativeImageLoader(Context context) {
        this.context = context;
        // 获取应用程序的最大内存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory());

        // 用最大内存的1/4来存储图片
        final int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

            // 获取每张图片的大小
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    return bitmap.getAllocationByteCount();
                }
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };
        initDiskCache(context);
    }

    /**
     * 初始化硬盘缓存
     *
     * @param context
     */
    public void initDiskCache(final Context context) {
        // Set up disk cache
        File diskCacheDir = getDiskCacheDir(context, "fileManage");
        if (diskCacheDir != null) {
            if (!diskCacheDir.exists()) {
                diskCacheDir.mkdirs();
            }
            try {
                mDiskCache = DiskLruCache.open(diskCacheDir,
                        SystemUtil.getAppVersionCode(context), 1, DISK_CACHE_SIZE);
            } catch (final IOException e) {
                diskCacheDir = null;
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过此方法来获取NativeImageLoader的实例
     *
     * @return
     */
    public static NativeImageLoader getInstance(Context context) {
        if (mInstance == null) {
            synchronized (NativeImageLoader.class) {
                if (mInstance == null) {
                    mInstance = new NativeImageLoader(context);
                }
            }
        }
        return mInstance;
    }

    /**
     * 加载本地图片，对图片不进行裁剪
     *
     * @param path
     * @param mCallBack
     * @return
     */
    public Bitmap loadNativeImage(final String path,
                                  final NativeImageCallBack mCallBack) {
        return this.loadNativeImage(0, path, null, mCallBack);
    }

    private boolean isRun = false;

    public void startBgThread() {
        ThreadPool.getInstance().getDefaultExecutor().execute(new Runnable() {

            @Override
            public void run() {
                while (isRun) {
                    if (mImageQueue != null && mImageQueue.size() > 0) {
                        LoadImageTask task = mImageQueue.pop();
                        runTask(task);
                        Message msg = mHander.obtainMessage(MSG_REQUEST, task);
                        mHander.sendMessage(msg);
                    } else {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        });
    }

    private void runTask(LoadImageTask task) {
        task.run();
    }

    /**
     * 此方法来加载本地图片，这里的mPoint是用来封装ImageView的宽和高，我们会根据ImageView控件的大小来裁剪Bitmap
     * 如果你不想裁剪图片，调用loadNativeImage(final String path, final NativeImageCallBack
     * mCallBack)来加载
     *
     * @param path
     * @param mPoint
     * @param mCallBack
     * @return
     */
    public Bitmap loadNativeImage(final int type, final String path,
                                  final Point mPoint, final NativeImageCallBack mCallBack) {
        // 先获取内存中的Bitmap
        Bitmap bitmap = getBitmapFromMemCache(path);
        mHander = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case MSG_REQUEST:

                        LoadImageTask task = (LoadImageTask) msg.obj;
                        mRequestQueue.remove(task);
                        if (mRequestQueue.size() > 0) {
                            mHander.sendMessage(mHander.obtainMessage(MSG_REQUEST,
                                    task));
                        } else if (mRequestQueue.size() == 0) {
                            // 所有的任务都运行结束了
                            isRun = false;

                            mHander.removeMessages(MSG_REQUEST);
                        }
                        break;
                    case MSG_lOAD_COMPLETED:

                        mCallBack.onImageLoader((Bitmap) msg.obj, path);
                        break;

                    default:
                        break;
                }
            }
        };
        // 若该Bitmap不在内存缓存中，则启用线程去加载本地的图片，并将Bitmap加入到mMemoryCache中
        if (bitmap == null) {
            LoadImageTask task = new LoadImageTask(path, type, mHander, mPoint);
            mImageQueue.push(task);
            mRequestQueue.add(task);
            if (!isRun) {
                isRun = true;
                startBgThread();
            }
        }
        return bitmap;
    }

    /**
     * 往缓存中添加Bitmap
     *
     * @param key
     * @param bitmap
     */
    private void addBitmapToCache(String key, Bitmap bitmap) {
        if (key == null || bitmap == null) {
            return;
        }
        addBitmapToMemCache(key, bitmap);
        addBitmapToDiskCache(key, bitmap);
    }

    /**
     * 往内存缓存中添加Bitmap
     *
     * @param data
     * @param bitmap
     */
    public void addBitmapToMemCache(String data, Bitmap bitmap) {
        mMemoryCache.put(data, bitmap);
    }

    /**
     * 往硬盘缓存中添加Bitmap
     *
     * @param data
     * @param bitmap
     */
    public void addBitmapToDiskCache(String data, Bitmap bitmap) {
        if (mDiskCache != null) {
            final String key = SystemUtil.hashKeyForDisk(data);
            OutputStream out = null;
            try {
                final DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                if (snapshot == null) {
                    final DiskLruCache.Editor editor = mDiskCache.edit(key);
                    if (editor != null) {
                        out = editor.newOutputStream(DISK_CACHE_INDEX);
                        bitmap.compress(COMPRESS_FORMAT, COMPRESS_QUALITY, out);
                        editor.commit();
                        out.flush();
                        out.close();
                        flush();
                    }
                } else {
                    snapshot.getInputStream(DISK_CACHE_INDEX).close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
                // 如果目录不存在，重建缓存目录
                File diskCacheDir = getDiskCacheDir(context, "fileManage");
                if (diskCacheDir != null) {
                    if (!diskCacheDir.exists()) {
                        diskCacheDir.mkdirs();
                    }
                }
                // initDiskCache(context);
            } finally {
                try {
                    if (out != null) {
                        out.close();
                        out = null;
                    }
                } catch (final IOException e) {

                } catch (final IllegalStateException e) {

                }
            }
        }
    }

    /**
     * 将缓存记录同步到journal文件中。
     */
    public void flush() {
        if (mDiskCache != null) {
            try {
                mDiskCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据key来获取内存中的图片
     *
     * @param key
     * @return
     */
    private Bitmap getBitmapFromMemCache(String key) {
        Bitmap bitmap = mMemoryCache.get(key);
        return bitmap;
    }

    /**
     * 根据View(主要是ImageView)的宽和高来获取图片的缩略图
     *
     * @param path
     * @param viewWidth
     * @param viewHeight
     * @return
     */
    private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight) {
        if (viewHeight == 0 || viewWidth == 0) {
            return BitmapFactory.decodeFile(path);
        } else {
            BitmapFactory.Options options = new BitmapFactory.Options();
            // 设置为true,表示解析Bitmap对象，该对象不占内存
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, options);
            // 设置缩放比例
            options.inSampleSize = computeScale(options, viewWidth, viewHeight);
            options.inPreferredConfig = Bitmap.Config.RGB_565;

            // 设置为false,解析Bitmap对象加入到内存中
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFile(path, options);
        }
    }

    private Bitmap decodeThumbBitmapForResource(int id, int viewWidth, int viewHeight) {
        if (viewHeight == 0 || viewWidth == 0) {
            return BitmapFactory.decodeResource(context.getResources(), id);
        } else {
            BitmapFactory.Options options = new BitmapFactory.Options();
            // 设置为true,表示解析Bitmap对象，该对象不占内存
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(context.getResources(), id, options);
            // 设置缩放比例
            options.inSampleSize = computeScale(options, viewWidth, viewHeight);

            // 设置为false,解析Bitmap对象加入到内存中
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeResource(context.getResources(), id, options);
        }
    }

    /**
     * 根据View(主要是ImageView)的宽和高来计算Bitmap缩放比例。默认不缩放
     *
     * @param options
     * @param viewWidth
     * @param viewHeight
     */
    private int computeScale(BitmapFactory.Options options, int viewWidth,
                             int viewHeight) {
        int inSampleSize = 1;
        if (viewWidth == 0 || viewHeight == 0) {
            return inSampleSize;
        }
        int bitmapWidth = options.outWidth;
        int bitmapHeight = options.outHeight;

        // 假如Bitmap的宽度或高度大于我们设定图片的View的宽高，则计算缩放比例
        if (bitmapWidth > viewWidth || bitmapHeight > viewWidth) {
            int widthScale = Math.round((float) bitmapWidth / (float) viewWidth);
            int heightScale = Math.round((float) bitmapHeight / (float) viewWidth);

            // 为了保证图片不缩放变形，我们取宽高比例最小的那个
            inSampleSize = widthScale < heightScale ? widthScale : heightScale;
        }
        return inSampleSize;
    }

    /**
     * 加载本地图片的回调接口
     *
     * @author xiaanming
     */
    public interface NativeImageCallBack {
        /**
         * 当子线程加载完了本地的图片，将Bitmap和图片路径回调在此方法中
         *
         * @param bitmap
         * @param path
         */
        public void onImageLoader(Bitmap bitmap, String path);
    }

    public static File getDiskCacheDir(Context context, String uniqueName) {

        // Check if media is mounted or storage is built-in, if so, try and use
        // external cache dir
        // otherwise use internal cache dir
        final String cachePath = context.getCacheDir().getPath();

        return new File(cachePath + File.separator + uniqueName);
    }

    public final Bitmap getBitmapFromDiskCache(final String data) {
        if (data == null) {
            return null;
        }
        Bitmap bitmap = null;
        FileDescriptor fileDescriptor = null;
        final String key = SystemUtil.hashKeyForDisk(data);// md5生成key
        if (mDiskCache != null) {
            FileInputStream inputStream = null;
            try {
                final DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                if (snapshot != null) {
                    inputStream = (FileInputStream) snapshot
                            .getInputStream(DISK_CACHE_INDEX);
                    fileDescriptor = inputStream.getFD();
                    if (fileDescriptor != null) {
                        bitmap = BitmapFactory
                                .decodeFileDescriptor(fileDescriptor);
                    }
                    if (bitmap != null) {
                        return bitmap;
                    }
                }
            } catch (final IOException e) {
                e.printStackTrace();
            } catch (OutOfMemoryError e) {
                System.gc();

            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }

    public class LoadImageTask implements Runnable {
        String path;
        int type;
        Handler handler;
        Point point;

        public LoadImageTask(String path, int type, Handler handler,
                             Point point) {
            this.path = path;
            this.type = type;
            this.handler = handler;
            this.point = point;
        }

        @Override
        public void run() {
            Bitmap bitmap = null;
            bitmap = getBitmapFromDiskCache(path);
            // 先获取图片的缩略图
            if (bitmap == null) {
                if (type == 0) {
                    bitmap = decodeThumbBitmapForFile(path, point == null ? 0
                            : point.x, point == null ? 0 : point.y);
                } else if (type == 1) {
                    bitmap = SystemUtil.getVideoThumbnail(path);
                    if (bitmap == null) {
                        bitmap = decodeThumbBitmapForResource(
                                R.drawable.friends_sends_pictures_no,
                                point == null ? 0 : point.x,
                                point == null ? 0 : point.y);
                    }
                }
            }
            Message msg = handler.obtainMessage();
            msg.obj = bitmap;
            msg.what = MSG_lOAD_COMPLETED;
            handler.sendMessage(msg);
            // 将图片加入到缓存中
            addBitmapToCache(path, bitmap);
        }
    }
}
